# -*- coding: utf-8 -*-


import sys, time
from PySide6.QtSerialPort import QSerialPort, QSerialPortInfo
from PySide6.QtCore import *
import serial
import serial.tools.list_ports
import numpy as np

import multiprocessing
import struct


MAITAIN_WORK_FAREM = 0x30 # 维护工作帧 */     
MAITAIN_BITCFG_SET = 0x34 # BIT配置 */
MAITAIN_DEVCFG_SET = 0x35 # 参数配置 */ 
MAITAIN_USERCFG_SET = 0x36 # 用户参数 */
MAITAIN_DEVLOG_READ = 0x37 # 设备日志读取 */
MAITAIN_DEVINFO = 0x61 # 设备信息读取 */ 
MAITAIN_BITCFG_GET = 0x64 # BIT配置读取 */
MAITAIN_DEVCFG_GET = 0x65 # 参数配置读取 */
MAITAIN_USERCFG_GET = 0x66 # 用户参数读取 */
MAITAIN_OTA = 0X59 # 在线升级 */
MAITAIN_OTA_ACK = 0X89 #在线升级反馈 */

def str_to_int_safe(s):
    if s == "":
        var = 0
    else:
        var = int(float(s))
    return var


def str_to_float_safe(s, a):
    if s == "":
        var = 0
    else:
        var = int(float(s)*a)
    return var


def set_bit(number, n):
    return number | (1 << n)
    
def get_bit(number, n):
    return (number >> n) & 1
    
def clear_bit(number, n):
    return number & ~(1 << n)


def GetCheckWord(p, length) :
    i = 0
    Xor_Temp = int(0)
    Sum_Temp = int(0)
    CheckWord = int(0)

    # 异或
    for i in range(length - 2):
       Xor_Temp = (Xor_Temp ^ (int)(p[ i ] & 0xff))&0xff
    # 和 
    for i in range(length - 2):
       Sum_Temp = (Sum_Temp + (int)(p[ i ] & 0xff))&0xffff
    
    Sum_Temp = (Sum_Temp + Xor_Temp) &0xffff
    # 0-前面所有字节之和 
    Sum_Temp =  (0 - (Sum_Temp & 0xff))&0xffff; # 反码+1
    CheckWord = ((Sum_Temp << 8) | Xor_Temp)&0xffff
    return CheckWord;

def crc8_cal(data, nbytes):
    crc8_table = [\
    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
    0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
    0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
    0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
    0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
    0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
    0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
    0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
    0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
    0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
    0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
    0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
    0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
    0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
    0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
    0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
    0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3\
    ]
    crc = np.uint8(0) 
    for i in range(nbytes): 
        crc = np.uint8(crc8_table[ crc ^ data[i]])
    return crc

class Matain_trans: 
    def __init__(self):
        self.trans_flag = 0
        self.pack_sta_req_sfcnt = 0
        self.data = np.zeros(64, dtype=np.uint8)
        self.data[0] = 0xAA # # 0 帧头1
        self.data[1] = 0x55 # # 1 帧头2
        self.data[2] = 0x0 # 
        self.data[3] = 0x1  
        self.data[4] = 0x4
          
    def start_pack(self, isleft):
        self.data[2] = self.pack_sta_req_sfcnt
        self.pack_sta_req_sfcnt = self.pack_sta_req_sfcnt+1
        if isleft:
            self.data[10] = 1
        else:
            self.data[10] = 2
        self.data[63] = crc8_cal(self.data, 63)
        
    def start_pack_l(self):
        self.start_pack(1)
        return self.data
        
    def start_pack_r(self):
        self.start_pack(0)
        return self.data
        
    def stop_pack(self):
        ret_array = np.array([0x10] * 200, dtype=np.uint8)
        return ret_array
     
    def set_trans_flag(self, flag):
        self.trans_flag = flag
        

def __init__(self, parm, parent=None):
    super(UpdateDataThread, self).__init__(parent)
    self.userSignal.connect(self.handle_signal)
    self.is_exit = False
    self.parm = parm
    
def handle_signal(self, val):
    # 处理信号传递的参数
    self.parm.dump_en = val


class SerThread(QThread):
    # QTthread 第2给形参是
    #_signal_update = pyqtSignal(str)  # 信号
    #userSignal = pyqtSignal(int)

    def __init__(self,   ui, parent=None):
        super().__init__()
        self.ui = ui
        self.rlen = 0
        self.last_rlen = 0
        self.serdev = Kser(ui)
        self.trans = Matain_trans()
        self.dump_en = False
        self.timer_disable = False 
        self.timer_req1s = QTimer()
        self.timer_req1s.timeout.connect(self.req1s)
        self.timer_req1s.start(1000)
        content = "已接收：{: >4d}".format(self.rlen)+ "B"
        self.ui.label_12.setText(content)  
        
    def parse_maitain(self, data):
        if data[5] != 0x24 and data[5] != 0x10:
            print("parse err")
            return
        else:
            type = data[6]
            if type == MAITAIN_WORK_FAREM:
                self.ui.devsta.decode_sta(data)
            elif type == MAITAIN_BITCFG_GET:
                self.ui.bitcfg.decode(data)
            elif type == MAITAIN_DEVCFG_GET:
                self.ui.devcfg.decode(data)
            elif type == MAITAIN_USERCFG_GET:
                self.ui.usercfg.decode(data)  
            elif type == MAITAIN_DEVLOG_READ:
                self.ui.devlog.decode(data) 
            elif type == MAITAIN_OTA_ACK:
                self.ui.ota.decode(data)    
            else:
                pass

    def verify(self, data):
        ret = 0
        len = data[2]
        verify1 = GetCheckWord(data, len)
        verify2 = int(data[len-2]) + int(data[len-1]) * 256
        if verify1 != verify2:
            print("verify err: {:0>4x}/{:0>4x}".format(verify1, verify2))
            return -1   
        # print("verify ok: {:0>4x}/{:0>4x}".format(verify1, verify2))    
        return 0

    def recv_msg_highspeed(self):
        buflen = self.serdev.ser.in_waiting
        blocklen = self.ui.highspeed.dt_framelen * 200 # 缓冲大于200帧时开始处理
        if buflen > (blocklen + 15) :
            head = self.serdev.ser.read(blocklen)
            idx = self.ui.highspeed.find_head(head[0:])
            left_len = self.ui.highspeed.parse_block(head[idx:])
            if(left_len > 0 and left_len < 15) :
                drop_len = 15 - left_len
                self.serdev.ser.read(drop_len)
                print("left_len", left_len, "len", len(head), "droped", drop_len)
            return len(head)
        return 0

    def recv_blcok(self): 
        wait = 1
        last_rlen = 0
        rlen = 0
        #flag = 0
        #flagt1 = int(time.perf_counter() * 1000)

        while wait > 0:
            rlen = self.serdev.ser.in_waiting
            wait-=1
            if rlen <= 0:
                rlen = 0          
            elif rlen >  last_rlen:
                last_rlen = rlen
                wait = 10
                flag = 1
            time.sleep(0.002)
            
        #if(flag == 1):
        #    flagt2 = int(time.perf_counter() * 1000)
        #    print("reln", rlen,  "wait", flagt2 - flagt1)  # 获取当前时间（高精度）)
        return rlen
    def recv_msg_maitain(self):        
        buflen = self.recv_blcok()
        if buflen >= 1 :
            a = self.serdev.ser.read(buflen)
            data_len = len(a)
            i = 0
            while i < data_len - 7:
                if(a[i]==0xAA and a[i+1]==0x55 and a[i+2] >= 7):
                    if 0 != self.verify(a[i:]):
                        i+=1
                        continue
                    else:
                        self.parse_maitain(a[i:])
                        i+=a[i+2]
                else:
                    i+=1 
            return data_len
        else:
            return 0
                
    def recv_msg(self):
        if True == self.dump_en:
            rlen = self.recv_msg_highspeed()
        else:
            rlen = self.recv_msg_maitain()

        if rlen > 0:
            self.rlen+= rlen
            if self.rlen >= 1000*1000:
                content = "已接收：{: >3.1f}".format(self.rlen/1024/1024) + "MB"
            elif self.rlen >= 1000:
                content = "已接收：{: >3.1f}".format(self.rlen/1024)+ "KB"
            else:
                content = "已接收：{: >4d}".format(self.rlen)+ "B"
            self.ui.label_12.setText(content)        

        
    def run(self):
        while True:
            if self.serdev.ser.isOpen():
                try:
                    self.recv_msg()
                except:
                    print("frame err")
                    time.sleep(0.1)  
            else:        
                time.sleep(0.1)
       
    def req1s(self):
        if self.serdev.ser.isOpen():
            if self.trans.trans_flag == 0:
                if( self.timer_disable == True):
                    return
                array = self.pack_frame_head(0x24, 0x30)
                self.pack_frame_tail(array)
                self.serdev.ser.write(array)
            else:
                if self.trans.trans_flag == 1:
                    array = self.trans.start_pack_l()
                elif self.trans.trans_flag == 2:
                    array = self.trans.start_pack_r()
                else:
                    array = self.trans.stop_pack()
                self.trans.trans_flag = 0    
                # print(' '.join(f'{c:02x}' for c in array))
                self.serdev.ser.write(array)
        else:
            pass
             
    def pack_frame_head(self, syspart, frame_type):
        self.pack_sta_req_sfcnt = 0
        data = np.zeros(64, dtype=np.uint8)
        data[0] = 0xAA # # 0 帧头1 */
        data[1] = 0x55 # # 1 帧头2 */
        data[2] = 64 # # 2 帧长度 */
        data[3] = self.pack_sta_req_sfcnt # /* 3 发送帧计数 */
        self.pack_sta_req_sfcnt = (self.pack_sta_req_sfcnt + 1)&0xff
        data[5] = syspart # /* 5 系统/组件 */
        data[6] = frame_type # /* 6 帧类型 */
        return data

    def pack_frame_tail(self, a):
        verify = GetCheckWord(a, 64)
        a[62] = verify & 0xff
        a[63] = verify >> 8
        
    def stop(self):
        # 停止线程的方法
        self.quit()
        self.wait()




class Kser:
    def __init__(self, ui):
        self.ui = ui 
        self.UIPort_comboBox = ui.comboBox # 串口号复选框
        self.UIBound_comboBox = ui.comboBox_2 # 波特率复选框
        self.UIOpen_pushButton = ui.pushButton # 打开按钮

        self.UIPort_comboBox.activated.connect(self.port_check)
        self.UIOpen_pushButton.clicked.connect(self.port_open)
        self.UIPort_comboBox.currentIndexChanged.connect(self.port_close)
        self.UIBound_comboBox.currentIndexChanged.connect(self.port_close)
  
        self.ser = serial.Serial()
        self.ser.set_buffer_size(40960)
        self.ser.bytesize = 8 # 8数据位
        self.ser.stopbits = 1 # 1停止位
        self.ser.parity = "N" # 无校验
        
        self.port_dict = {}
        self.port_check() # 检测所有存在的串口，将信息存储在字典中 
        #self.port_open() # 调试的时候默认打开
 
    # 串口检测
    def port_check(self):
        refresh_str = "检测串口"
        if self.UIPort_comboBox.currentText() == refresh_str or self.UIPort_comboBox.currentText() == "":
            port_list = list(serial.tools.list_ports.comports())
            self.UIPort_comboBox.clear()
            last_com = self.ui.cfg.data['COM']
            sel_com = ""
            for port in port_list:
                self.port_dict["%s" % port[0]] = "%s" % port[1]
                self.UIPort_comboBox.addItem(port[0])
                if(port[0] == last_com):
                    sel_com = last_com    
            self.UIPort_comboBox.addItem(refresh_str)
            # 调试用
            self.UIPort_comboBox.setCurrentText(sel_com)

    def port_close(self):
        print("port_close")
        self.UIOpen_pushButton.setText("连接设备")
        if self.ser.isOpen(): 
            try:
                self.ser.close()
            except:
                pass
  
    # 串口打开        
    def port_open(self):
        if self.ser.isOpen():
            try:
                self.ser.close()
                self.UIOpen_pushButton.setText("连接设备")
            except:
                pass
        else :
            self.ser.port = self.UIPort_comboBox.currentText()
            self.ser.baudrate = int(self.UIBound_comboBox.currentText())
            try:
                self.ser.open()
            except:
                return None
            if self.ser.isOpen(): 
                self.UIOpen_pushButton.setText("断开连接")
                if self.ser.port != self.ui.cfg.data['COM'] or self.ser.baudrate != self.ui.cfg.data['baudrate']:
                    self.ui.cfg.data['COM'] = self.ser.port
                    self.ui.cfg.data['baudrate'] = self.ser.baudrate
                    self.ui.cfg.save()
  




